Procesamiento de Señales e Imágenes (PSIM) Una introducción para 10.º–11.º: ver, escuchar y entender el mundo con datos.
Propósito del taller
Explorar qué es una señal (por ejemplo: audio, ritmo cardiaco, acelerómetro) y qué es una imagen (fotografía, rayos X).
Comprender, con ejemplos cotidianos, cómo se limpia, analiza y transforma la información para tomar mejores decisiones.
Probar una actividad práctica donde generamos y mejoramos una señal y una imagen con código visible y simple.
Enfoques didácticos sugeridos (K-12)
Aprendizaje por indagación (modelo 5E) con recursos de NASA eClips (guías con actividades alineadas a estándares). Ver “Educator Guides” en NASA eClips. https://nasaeclips.arc.nasa.gov/resources/guides
Cómo ‘se ve’ un filtro: difuminado (promedio 3×3) vs. detección de bordes (Sobel-X).
Demo 3 — Una imagen sintética y sus bordes (matplotlib)
import numpy as npimport matplotlib.pyplot as plt# Imagen sintética (tablero + círculo) con ruidoH, W =128, 128yy, xx = np.indices((H, W))checker = (((yy//8) + (xx//8)) %2)*180circle = ((yy -64)**2+ (xx -64)**2) < (28**2)img = checker.copy().astype(float)img[circle] =240rng = np.random.default_rng(5)img_noisy = img + rng.normal(0, 12, size=img.shape)img_noisy = np.clip(img_noisy, 0, 255)# Convolución 2D (padding simple) con kernel promedio 3x3kernel = np.ones((3,3))/9.0pad =1padded = np.pad(img_noisy, pad_width=pad, mode='edge')blur = np.zeros_like(img_noisy)for i inrange(H):for j inrange(W): region = padded[i:i+3, j:j+3] blur[i, j] = np.sum(region * kernel)# Detección de bordes (Sobel)sx = np.array([[-1,0,1], [-2,0,2], [-1,0,1]])sy = np.array([[-1,-2,-1], [ 0, 0, 0], [ 1, 2, 1]])def conv2(img, k): pad = k.shape[0]//2 p = np.pad(img, pad, mode='edge') out = np.zeros_like(img, dtype=float)for i inrange(img.shape[0]):for j inrange(img.shape[1]): out[i,j] = np.sum(p[i:i+k.shape[0], j:j+k.shape[1]] * k)return outgx = conv2(blur, sx)gy = conv2(blur, sy)edges = np.hypot(gx, gy)edges = edges / (edges.max() +1e-9) *255fig, axs = plt.subplots(1,3)axs[0].imshow(img_noisy, cmap='gray', vmin=0, vmax=255)axs[0].set_title("Original con ruido")axs[1].imshow(blur, cmap='gray', vmin=0, vmax=255)axs[1].set_title("Suavizada")axs[2].imshow(edges, cmap='gray', vmin=0, vmax=255)axs[2].set_title("Bordes")for a in axs: a.axis('off')plt.tight_layout()plt.show()
Imagen original, versión suavizada y bordes detectados.
Actividad práctica (se ejecuta al compilar)
Reto: Ajusta un par de variables, vuelve a compilar y observa cómo cambian la señal y la imagen. La intención es mejorar la claridad sin “pasarte”: si suavizas demasiado, pierdes detalle.
import numpy as npimport matplotlib.pyplot as plt# === Parámetros que puedes cambiar ===win =13# tamaño del promedio móvil para la señal (prueba 5, 9, 13, 21)kimg =5# tamaño del kernel cuadrado para suavizar la imagen (prueba 3, 5, 7)# --- Señal ---rng = np.random.default_rng(123)fs =200t = np.linspace(0, 2, fs*2, endpoint=False)signal_clean =0.6*np.sin(2*np.pi*4*t) +0.2*np.sin(2*np.pi*11*t)noise =0.45*rng.normal(size=t.size)x = signal_clean + noisedef moving_average(x, w=7): k = np.ones(max(1,int(w)))/max(1,int(w))return np.convolve(x, k, mode='same')xs = moving_average(x, win)# --- Imagen ---H, W =120, 120yy, xx = np.indices((H, W))rect = ((yy>30)&(yy<90)&(xx>45)&(xx<75))*220grad = (xx/xx.max())*180img = grad.copy()img[rect>0] =240img += rng.normal(0, 15, size=img.shape)img = np.clip(img, 0, 255)# Kernel promedio kimg×kimgk =max(3, int(kimg))if k %2==0: k +=1ker = np.ones((k,k))/ (k*k)pad = k//2p = np.pad(img, pad, mode='edge')blur = np.zeros_like(img)for i inrange(H):for j inrange(W): blur[i,j] = np.sum(p[i:i+k, j:j+k] * ker)# Figurasfig = plt.figure(figsize=(10,6))gs = fig.add_gridspec(2,2)ax1 = fig.add_subplot(gs[0, :])ax1.plot(t, x, lw=1, label='Con ruido')ax1.plot(t, xs, lw=2, label=f'Suavizada (w={win})')ax1.set_title("Señal: antes vs. después")ax1.set_xlabel("Tiempo (s)"); ax1.set_ylabel("Amplitud (relativa)")ax1.legend()ax2 = fig.add_subplot(gs[1, 0])ax2.imshow(img, cmap='gray', vmin=0, vmax=255)ax2.set_title("Imagen ruidosa")ax2.axis('off')ax3 = fig.add_subplot(gs[1, 1])ax3.imshow(blur, cmap='gray', vmin=0, vmax=255)ax3.set_title(f"Imagen suavizada (kernel {k}×{k})")ax3.axis('off')plt.tight_layout()plt.show()
Ajusta las variables y recompila: compara resultados.
Instrucciones para el aula (rápidas):
Cambia win (señal) y kimg (imagen) en el bloque anterior.
Vuelve a compilar/renderizar.
En parejas, respondan:
¿Qué valor “mejor equilibra” claridad y detalle en la señal?
¿Qué valor “mejor equilibra” claridad y detalle en la imagen?
¿En qué casos suavizar demasiado es un problema?
Cierre (ideas clave)
Señales: historias en el tiempo; Imágenes: mosaicos de píxeles.
Filtros simples ayudan a ver mejor: suavizar, resaltar bordes, ajustar color.
Exceso de suavizado borra detalles; insuficiente deja ruido.
La intuición visual es el primer paso antes de técnicas avanzadas.